#include <assert.h>
#include "app_config.h"
#include "app_audio.h"
#include "audio_scene.h"
#include "co_list.h"
#include "fr_device_pa.h"
#include "heap.h"


#define APP_AUDIO_DATA_READY_THD            6
#define APP_AUDIO_DATA_BUFFER_MAX           12

enum app_audio_mode_t {
    APP_AUDIO_MODE_IDLE,
    APP_AUDIO_MODE_A2DP_SINK,
    APP_AUDIO_MODE_A2DP_SOURCE,
    APP_AUDIO_MODE_SCO,
    APP_AUDIO_MODE_TONE,
    APP_AUDIO_MODE_VOICE_RECOGNIZE,
};

static enum app_audio_mode_t audio_mode =  APP_AUDIO_MODE_IDLE;
static uint32_t audio_data_counter = 0;
static audio_scene_t *audio_scene;

static uint32_t tone_offset = 0;
static uint32_t tone_single_size = 128;
static uint8_t *tone_data;
static uint32_t tone_size;

static uint32_t mp3_rd_offset = 0;
#define A2DP_SOURCE_MP3_RAW_DATA_LENGTH         512     //the length transmit to decoder each time

static void audio_pa_disable(void)
{
    device_pa_disable();
    #if 0
    printf("MEM usage\r\n \
                        \tHEAP_TYPE_SRAM_BLOCK: %d, %d\r\n \
                        \tHEAP_TYPE_DRAM_BLOCK: %d, %d\r\n \
                        \tHEAP_TYPE_BTDM_BLOCK: %d, %d\r\n \
                        \tTOTAL USAGE: %d\r\n", \
                            heap_get_mem_usage(HEAP_TYPE_SRAM_BLOCK), heap_get_max_mem_usage_single(HEAP_TYPE_SRAM_BLOCK), \
                            heap_get_mem_usage(HEAP_TYPE_DRAM_BLOCK), heap_get_max_mem_usage_single(HEAP_TYPE_DRAM_BLOCK), \
                            heap_get_mem_usage(HEAP_TYPE_BTDM_BLOCK), heap_get_max_mem_usage_single(HEAP_TYPE_BTDM_BLOCK), \
                            heap_get_max_mem_usage());
    #endif
}

static void audio_pa_enable(void)
{
    device_pa_enable();
    #if 0
    printf("MEM usage\r\n \
                        \tHEAP_TYPE_SRAM_BLOCK: %d, %d\r\n \
                        \tHEAP_TYPE_DRAM_BLOCK: %d, %d\r\n \
                        \tHEAP_TYPE_BTDM_BLOCK: %d, %d\r\n \
                        \tTOTAL USAGE: %d\r\n", \
                            heap_get_mem_usage(HEAP_TYPE_SRAM_BLOCK), heap_get_max_mem_usage_single(HEAP_TYPE_SRAM_BLOCK), \
                            heap_get_mem_usage(HEAP_TYPE_DRAM_BLOCK), heap_get_max_mem_usage_single(HEAP_TYPE_DRAM_BLOCK), \
                            heap_get_mem_usage(HEAP_TYPE_BTDM_BLOCK), heap_get_max_mem_usage_single(HEAP_TYPE_BTDM_BLOCK), \
                            heap_get_max_mem_usage());
    #endif
}

__attribute__((section("dram_section"))) uint8_t tone_buffer[128];
void request_raw_tone_cb(uint8_t **data, uint32_t *length, bool *comp)
{
    printf("offset %d , size %d\n",tone_offset,tone_size);
    if((tone_offset + tone_single_size) <= tone_size){
        //*data = &tone_data[tone_offset];
        memcpy(tone_buffer,&tone_data[tone_offset],tone_single_size);
        *data = tone_buffer;
        *length = tone_single_size;
        *comp = false;
    }
    else{
        //*data = &tone_data[tone_offset];
        *length = tone_size - tone_offset;
        memcpy(tone_buffer,&tone_data[tone_offset],tone_size - tone_offset);
        *data = tone_buffer;
        *comp = true;         
    }
    tone_offset += *length; 
}

void request_raw_mp3_cb(uint8_t **data, uint32_t *length, bool *comp)
{
    #if 0
    uint32_t total_length = mp3_sample_get_size();
    
    if((mp3_rd_offset + A2DP_SOURCE_MP3_RAW_DATA_LENGTH) <= total_length){
        *data = &mp3_sample[mp3_rd_offset];
        *length = A2DP_SOURCE_MP3_RAW_DATA_LENGTH;
        *comp = false;
        mp3_rd_offset += *length;    
    }
    else{
        *data = &mp3_sample[mp3_rd_offset];
        *length = total_length - mp3_rd_offset;
        *comp = false;  // repeat mp3 sample  
        mp3_rd_offset = 0;    
    }
    #endif
}


static void app_audio_stop(void)
{
    switch(audio_mode) {
        case APP_AUDIO_MODE_A2DP_SINK:
            app_audio_a2dp_sink_stop();
            break;
        case APP_AUDIO_MODE_SCO:
            app_audio_sco_stop();
            break;
        case APP_AUDIO_MODE_A2DP_SOURCE:
            app_audio_a2dp_source_stop();
            break;
        default:
            break;
    }
}

void app_audio_a2dp_sink_start(audio_type_t audio_type, uint32_t sample_rate)
{
    audio_scene_param_a2dp_sink_t param;

    if ((audio_mode == APP_AUDIO_MODE_A2DP_SINK) || (audio_mode == APP_AUDIO_MODE_A2DP_SOURCE)) {
        return;
    }
    
    if (audio_mode != APP_AUDIO_MODE_IDLE) {
        app_audio_stop();
    }

    param.sample_rate = sample_rate;
    param.channels = 2;
    param.audio_type = audio_type;
    param.hw_type = AUDIO_HW_TYPE_CODEC;
    param.hw_base_addr = I2S0_BASE;

    assert(audio_mode == APP_AUDIO_MODE_IDLE);

    audio_scene = audio_scene_create(AUDIO_SCENE_TYPE_A2DP_SINK, &param);
    assert(audio_scene != NULL);
    audio_mode = APP_AUDIO_MODE_A2DP_SINK;
}

void app_audio_a2dp_sink_stop(void)
{
    if (audio_mode == APP_AUDIO_MODE_A2DP_SINK) {        
        audio_scene_destroy(audio_scene);
        audio_mode = APP_AUDIO_MODE_IDLE;
        audio_pa_disable();
    }
}

void app_audio_a2dp_sink_play(uint8_t *buffer, uint32_t length)
{
    if (audio_mode == APP_AUDIO_MODE_A2DP_SINK) {
        if (audio_scene_dac_is_ready(audio_scene)) {
            audio_pa_enable();
        }
        audio_scene_recv_raw_data(audio_scene, true, buffer, length);
    }
}

void app_audio_sco_start(audio_type_t audio_type,audio_sence_report_encoded_frame report_enc_cb, void *report_enc_arg)
{
    audio_scene_param_sco_t param;
    
    if (audio_mode == APP_AUDIO_MODE_SCO) {
        return;
    }
    
    if (audio_mode != APP_AUDIO_MODE_IDLE) {
        app_audio_stop();
    }

    if (audio_type == AUDIO_TYPE_MSBC) {
        printf("SCO: msbc.\r\n");
        param.sample_rate = 16000;
    }
    else {
        printf("SCO: cvsd.\r\n");
        param.sample_rate = 8000;
    }
    param.audio_type = audio_type;
    param.hw_type = AUDIO_HW_TYPE_CODEC;
    param.hw_base_addr = I2S0_BASE;
    param.report_enc_cb = report_enc_cb;
    param.report_enc_arg = report_enc_arg;

    assert(audio_mode == APP_AUDIO_MODE_IDLE);

    audio_scene = audio_scene_create(AUDIO_SCENE_TYPE_SCO, &param);
    audio_mode = APP_AUDIO_MODE_SCO;
    assert(audio_scene != NULL);
}

void app_audio_sco_stop(void)
{
    if (audio_mode == APP_AUDIO_MODE_SCO) {        
        audio_scene_destroy(audio_scene);
        audio_mode = APP_AUDIO_MODE_IDLE;
        audio_pa_disable();
    }
}

void app_audio_sco_recv(bool valid, uint8_t audio_type, uint8_t *buffer, uint32_t length)
{
    if (audio_mode == APP_AUDIO_MODE_SCO) {
        if (audio_scene_dac_is_ready(audio_scene)) {
            audio_pa_enable();
        }
        if (audio_type == AUDIO_TYPE_MSBC) {
            audio_scene_recv_raw_data(audio_scene, valid, buffer+2, length-3);
        }
        else {
            audio_scene_recv_raw_data(audio_scene, valid, buffer, length);
        }
    }
}

void app_audio_a2dp_source_start(audio_type_t audio_type, a2dp_sbc_param_t *sbc_param, audio_sence_report_encoded_frame report_enc_cb, void *report_enc_arg)
{
    audio_scene_param_a2dp_source_t param;

    if (audio_mode == APP_AUDIO_MODE_A2DP_SOURCE) {
        return;
    }
    
    if (audio_mode != APP_AUDIO_MODE_IDLE) {
        app_audio_stop();
    }

    memcpy(&param.sbc_param,sbc_param,sizeof(param.sbc_param));
//    param.sbc_param = sample_rate;
    param.channels = 2;
    param.audio_input_type = audio_type;
    param.audio_output_type = AUDIO_TYPE_SBC;
    param.hw_type = AUDIO_HW_TYPE_I2S;
    param.hw_base_addr = I2S0_BASE;
    param.report_enc_cb = report_enc_cb;
    param.report_enc_arg = report_enc_arg;
    param.dec_req_raw_cb = request_raw_mp3_cb;
    
    assert(audio_mode == APP_AUDIO_MODE_IDLE);

    audio_scene = audio_scene_create(AUDIO_SCENE_TYPE_A2DP_SOURCE, &param);
    assert(audio_scene != NULL);
    audio_mode = APP_AUDIO_MODE_A2DP_SOURCE;
}

void app_audio_a2dp_source_stop(void)
{
    if (audio_mode == APP_AUDIO_MODE_A2DP_SOURCE) {        
        audio_scene_destroy(audio_scene);
        audio_mode = APP_AUDIO_MODE_IDLE;
    }
}

void app_audio_tone_stopped_cp(void)
{
    if (audio_mode == APP_AUDIO_MODE_TONE) {
        audio_mode = APP_AUDIO_MODE_IDLE;
    }
}

void app_audio_tone_play(audio_type_t audio_type, uint8_t *tone_sample, uint32_t tone_len)
{
    audio_scene_param_tone_t param;
    
    if (audio_mode == APP_AUDIO_MODE_TONE) {
        return;
    }
    
    tone_offset = 0;
    if(tone_sample == NULL){
        #if 1
        extern const uint8_t sbc_sample[];
        tone_data = sbc_sample;
        tone_size = sbc_sample_get_size();
        #endif
    }
    else{
        tone_data = tone_sample;
        tone_size = tone_len;
    }
    
    if (audio_mode == APP_AUDIO_MODE_IDLE) {
        param.audio_type = audio_type;
        param.hw_type = AUDIO_HW_TYPE_CODEC;
        param.hw_base_addr = I2S0_BASE;
        param.dec_req_raw_cb = request_raw_tone_cb;
        param.report_stopped_cb = app_audio_tone_stopped_cp;
        assert(audio_mode == APP_AUDIO_MODE_IDLE);
        audio_scene = audio_scene_create(AUDIO_SCENE_TYPE_TONE, &param);
        audio_mode = APP_AUDIO_MODE_TONE;
        assert(audio_scene != NULL);
    }
    else{
        audio_scene_add_new_tone(audio_type,request_raw_tone_cb);
    }
}

void app_audio_tone_stop(void)
{
    if (audio_mode == APP_AUDIO_MODE_IDLE) {
        return;
    }

    if (audio_mode == APP_AUDIO_MODE_TONE) {
        audio_scene_destroy(audio_scene);
    }
    else{
        audio_scene_remove_tone();
    }
}
